package com.lottery.game.runner.v2;

import com.lottery.game.common.bean.BeanConverter;
import com.lottery.game.common.constants.GameConstants;
import com.lottery.game.common.utils.DateUtils;
import com.lottery.game.core.model.dto.game.GameTypePlayingLotteryRuleTO;
import com.lottery.game.core.model.dto.game.GameTypePlayingRelationTO;
import com.lottery.game.core.modules.entity.game.GameRoom;
import com.lottery.game.core.modules.entity.game.GameTypePlayingLotteryRule;
import com.lottery.game.core.modules.entity.user.GameIssue;
import com.lottery.game.core.modules.service.app.BetService;
import com.lottery.game.core.modules.service.app.OnlineAppContext;
import com.lottery.game.core.modules.service.app.WebSocketService;
import com.lottery.game.core.modules.service.app.WebsocketMessageService;
import com.lottery.game.core.modules.service.game.GameTypePlayingLotteryRuleService;
import com.lottery.game.core.modules.service.game.GameTypePlayingRelationService;
import com.lottery.game.core.modules.service.game.LockService;
import com.lottery.game.runner.v2.common.RoomCoreExecutorContextHolder;
import com.lottery.game.runner.v2.trigger.RoomOfflineTrigger;
import com.lottery.game.runner.v2.utils.RoomUtils;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import static com.lottery.game.common.utils.DateUtils.isCurrentTimeIn;

import java.util.*;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import lombok.extern.slf4j.Slf4j;

/**
 * Substitute com.runshang.apps.cloudgame.runner.CloudGameRunner
 * @author Jake
 *
 */
// 前情概要： 分布式系统对于同一个类型的房间只允许同一个线程去执行操作，
//               以免其他线程修改了数据，导致数据不一致的状况造成
// 1. 在执行之前，先访问数据库，查看有没有在执行的线程。
// 2. 如果已经有在执行的线程，那就不要新建线程了
// 3. 如果没有正在执行的线程，或者线程已经挂了，可以执行当前线程，然后应该把状态提交给数据库。
@Slf4j
@Component
public class GameThreadRunner{

	//unit: seconds
	private final Integer expireTime = 60 * 5;

	@Autowired
	private BetService betService;

	@Autowired
	private RoomUtils roomUtils;

	@Autowired
	private RoomOfflineTrigger offlineTrigger;

	@Autowired
	private RobotSpeakExecutor robotSpeakExecutor;

	@Autowired
	private WebSocketService webSocketService;

	@Autowired
	private OnlineAppContext context;

	@Autowired
	private WebsocketMessageService websocketMessageService;

	@Autowired
	private GameTypePlayingRelationService resourceClient;

	@Autowired
	private LockService lockService;

	@Autowired
	private GameTypePlayingLotteryRuleService gameTypePlayingLotteryRuleService;
	@Value("${server.port}")
	private Integer serverPort;

	/**
	 * Description: 再上一次执行完成后每过10秒去检测数据有没有在执行
	 * 第一次执行延时5秒
	 */
	@Scheduled(fixedDelay = 10000, initialDelay = 5000)
	public void run(){

		String requestId = serverPort + "";

		Boolean locked = lockService.tryLock(requestId, expireTime);
		log.info("---locked:  {}", locked);
		if(locked) {
			this.startMain();
		}

		log.info("当前时间：{}，主线程runner启动。。", DateUtils.getNowDateString());
	}

	/**
	 * 启动彩票28玩法
	 */
	public void startMain() {
		List<GameTypePlayingRelationTO> relations = resourceClient.findByGameTypeCode("LOTTERY");
		Set<String> startType = new HashSet<>();

		if(GameThreadDaemonContext.isFull()) {
			log.info("---game thread is full---");
			return;
		}

		List<GameTypePlayingRelationTO> list = resourceClient.all();

		if (CollectionUtils.isEmpty(list)) {
			return;
		}
		CountDownLatch countDownLatch = new CountDownLatch(list.size());
		for (GameTypePlayingRelationTO relation : list) {
			GameRoom gameRoom = new GameRoom();
			try {
		        String roomType = relation.getGameTypePlayingTicket() + "";
		        log.info("---roomType: {}", roomType);
		        if (startType.contains(roomType)) {
		            log.info("已有该彩种类型的房间在运行中跳过运行");
		            continue;
		        }
		        startType.add(roomType);
		        GameTypePlayingRelationTO relationTO = this.findByTicketType(relations, Integer.parseInt(roomType));
		        if (relationTO == null
		                || relationTO.getDurationTime() == null
		                || relationTO.getClosedTime() == null) {
		            log.error("游戏类型[{}]配置有问题,直接跳过线程启动", roomType);
		            continue;
		        }
		        gameRoom.setDuration(Math.toIntExact(relationTO.getDurationTime()));
		        gameRoom.setGameTime(Math.toIntExact(relationTO.getClosedTime()));
		        gameRoom.setMaintenance_start_time(relationTO.getMaintenanceStartTime());
		        gameRoom.setMaintenance_end_time(relationTO.getMaintenanceEndTime());
		        gameRoom.setRoomLotteryTicketType(relationTO.getGameTypePlayingTicket()+"");
				log.info("---roomType: {}, gameRoom: {}", roomType, gameRoom);

				log.info("---- GameThreadRunner: {} 类型 房间处理线程启动。。", roomType);
				RoomCoreParam param = new RoomCoreParam(roomType, gameRoom, false);

				RoomCoreExecutor executor = new RoomCoreExecutor(param,
						roomUtils,
						betService,
						offlineTrigger,
						robotSpeakExecutor,
						new GamblingClosuresExecutor(webSocketService, context),
						websocketMessageService);

				String currentExecutorkey = roomUtils.buildKey(roomType);
				RoomCoreExecutorContextHolder.put(currentExecutorkey,executor);
				this.startService(roomType, executor);

			} catch(Exception e) {
				log.error("game thread id: {}, name: {}", relation.getGameTypeId(), relation.getGameTypeName(), e);
			} finally {
				countDownLatch.countDown();
			}
		}
		try {
			countDownLatch.await();
			GameThreadDaemonContext.setFull();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}

	}

	private GameTypePlayingRelationTO findByTicketType(List<GameTypePlayingRelationTO> relations, Integer ticketType) {
	    for (GameTypePlayingRelationTO relationTO : relations) {
	        if (relationTO.getGameTypePlayingTicket().equals(ticketType)) {
	           return relationTO;
          }
      }
	    return null;
  }

	public void restart(GameRoom gameRoom, String singleType, Integer expect, Integer initTime) {
		if(initTime != null){
			//构建新线程
			gameRoom = new GameRoom();
			gameRoom.setRoomLotteryTicketType(singleType);
			//GameRoom gameRoom2 = betService.queryRoomType(gameRoom);
			GameTypePlayingRelationTO relations = resourceClient.findByTicket(Integer.valueOf(singleType));
			GameRoom gameRoom2 = new GameRoom();
	        gameRoom2.setDuration(Math.toIntExact(relations.getDurationTime()));
	        gameRoom2.setGameTime(Math.toIntExact(relations.getClosedTime()));
	        gameRoom2.setMaintenance_start_time(relations.getMaintenanceStartTime());
	        gameRoom2.setMaintenance_end_time(relations.getMaintenanceEndTime());
	        gameRoom2.setRoomLotteryTicketType(relations.getGameTypePlayingTicket()+"");
			
			//重启房间
			restartRoom(gameRoom2,singleType, expect, initTime);
			//如果有正在运行的相同期号，进行时间更新
			//int changSecond = initTime + gameRoom2.getDuration();
			int changSecond = initTime + relations.getClosedTime().intValue();
			//开奖时间为004期理论的开奖时间即 当前之间+210秒+提前封盘时间-游戏时间
			Calendar calendar = Calendar.getInstance();
			calendar.setTime(new Date());
			calendar.add(Calendar.SECOND, changSecond);
			GameIssue gameIssue = new GameIssue();
			gameIssue.setIssueApi(Integer.toString(expect));
			gameIssue.setStatus(GameConstants.GameIssueStatus_Running);
			Date openTime = calendar.getTime();
			gameIssue.setOpenTime(openTime);
			betService.tryUpdateIssueOpenTime(gameIssue);
		}



		/*List<GameTypePlayingRelationTO> relations = resourceClient.findByGameTypeCode("LOTTERY");
		List<GameTypePlayingRelationTO> list = resourceClient.all();

		if (CollectionUtils.isEmpty(list)) {
			return;
		}
		GameTypePlayingRelationTO relationTO = new GameTypePlayingRelationTO();
		for (GameTypePlayingRelationTO relation : list) {
			relationTO = this.findByTicketType(relations, Integer.parseInt(singleType));

		}
		if (relationTO == null
				|| relationTO.getDurationTime() == null
				|| relationTO.getClosedTime() == null) {
			log.error("游戏类型[{}]配置有问题,直接跳过线程启动", singleType);
		}

		RoomCoreParam param = new RoomCoreParam(singleType, true, expect, initTime);
		RoomCoreExecutor executor =
				new RoomCoreExecutor(param,
						roomUtils,
						betService,
						offlineTrigger,
						robotSpeakExecutor,
						new GamblingClosuresExecutor(webSocketService, context),
						websocketMessageService);

		String currentExecutorkey = roomUtils.buildKey(singleType);
		RoomCoreExecutor existThread = RoomCoreExecutorContextHolder.getThreadByKey(currentExecutorkey);

		// 强制关闭该线程，哪怕任务没结束
      if (existThread != null) {
          existThread.disallow();
      }
		// 给予2秒的休息时间再创建新的线程
		try {
			TimeUnit.SECONDS.sleep(2L);
		} catch (InterruptedException e) {
			// do nothing...
		}
		RoomCoreExecutorContextHolder.put(currentExecutorkey,executor);
		executor.start();
*/
		// 原来这里的userStop就不需要用了

	}

	public void stop(String singleType) {
		log.info("停止开奖，彩种type为======"+singleType);
		String currentExecutorkey = roomUtils.buildKey(singleType);
		RoomCoreExecutor existThread = RoomCoreExecutorContextHolder.getThreadByKey(currentExecutorkey);
		// 强制关闭该线程，哪怕任务没结束
		if (existThread != null) {
			existThread.disallow();
		}
	}

	/**
	  * 构建不同的线程去对不同类型的房间去做操作
	 * @param singleType
	 * @param executor
	 */
	private void startService(String singleType, RoomCoreExecutor executor) {
		log.info("   ----singleType: {} 开始执行...", singleType);
		ExecutorService service = Executors.newSingleThreadExecutor();
		service.execute(executor);
		GameThreadDaemonContext.addThread(service, executor);
	}

	public void restartRoom(GameRoom gameRoom,String roomType,int expect,int initTime){
		GameTypePlayingRelationTO relations = resourceClient.findByTicket(Integer.valueOf(roomType));
		if (relations == null
				|| relations.getDurationTime() == null
				|| relations.getClosedTime() == null) {
			log.error("游戏类型[{}]配置有问题,直接跳过线程启动", roomType);
		}
		log.info("重置开奖，当前彩种期数======"+expect+"===重置时间===="+initTime);
		String currentExecutorkey = roomUtils.buildKey(roomType);
		RoomCoreExecutor existThread = RoomCoreExecutorContextHolder.getThreadByKey(currentExecutorkey);
		// 强制关闭该线程，哪怕任务没结束
		if (existThread != null) {
			existThread.disallow();
		}
		
		RoomCoreParam param = new RoomCoreParam(roomType, true, expect, initTime,gameRoom,relations);
		RoomCoreExecutor executor =
				new RoomCoreExecutor(param,
						roomUtils,
						betService,
						offlineTrigger,
						robotSpeakExecutor,
						new GamblingClosuresExecutor(webSocketService, context),
						websocketMessageService);
		RoomCoreExecutorContextHolder.put(currentExecutorkey,executor);
		this.startService(roomType, executor);
	}

}
